/*  RocketOSCserial

    Implements a GUI interface to an Open Source Flight Control (OSFC) processor
    
    For more information on the OSFC project, please visit:
    http://

    This code is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
  
    This code is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
  
    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/

// *************************************************************
// Uses the Built-in Serial Library
// & Peter Lagers GUI 4 Processing library: http://www.lagers.org.uk/g4p/

import processing.serial.*;
import g4p_controls.*;

// *************************************************************
// Global Variables

// The Rocket object - This handles communications to the Rocket
// and holds the rocket values
rocketOSFC theRocket;

// Serial Port Baud Rate & termination character
int baud_rate =  38400;
char term = 10;

// The Alpha values for the enabled/disabled controls
int enabledAlpha = 255;
int disableAlpha = 100;

// The Non-GP4 Display fonts & their character widths
PFont mono, mono20;
int width10, width20;

// The display text box object - An "in-window" alternative for "println()"
textBox displayText;

// The position values for the non-GP4 text items
int[] timeBox = {20, 20};
int[] milliBox = {20, 60};
int[] modeBox = {20, 110};
int[] latBox = {575, 20};
int[] lonBox = {575, 60};
int[] altBox = {420, 20};
int[] aglBox = {420, 100};
int[] g_altBox = {420, 60};
int[] linkBox = {200, 20};
int[] holdBox = {200, 60};

// boolean values to indicate whether the corresponding text input fields
// are "dirty" - i.e. have been edited by the user & do not reflect the 
// "Rocket" data
boolean siteAltDirty = false;
boolean goAltitudeDirty = false; 
boolean stageAltDirty = false; 
boolean stageVelDirty = false; 
boolean mainAltDirty = false;

// boolean values to indicate whether the corresponding text input fields
// need to be updated the next time a settings message is returned.
// Typically true when edited value is sent (for confirmation) and at
// start-up to initialize
boolean siteAltUpdate = true;
boolean goAltUpdate = true;
boolean stageAltUpdate = true;
boolean stageVelUpdate = true;
boolean mainAltUpdate = true;
boolean displaySetting = false;

// *************************************************************
// setup Function

void setup() {
  
  size(720,615);
  frameRate(5);
  
  theRocket = new rocketOSFC(this);
  
  // Get the config file preferences
  get_prefs();
  
  mono = loadFont("Monaco-10.vlw");
  width10 = 6;
  mono20 = loadFont("Monaco-20.vlw");
  width20 = 12;
    
  displayText = new textBox(12, 20, 375, 340, 15, mono, color(0), color(255));
  
  // Instantiate the GUI elements
  createGUI();
  testEnable(false);
  armEnable(false);
  
  // Set up the Serial Port Menu with the current list of Serial ports:
  theRocket.doMenu(serialPortMenu, connectStatus); 
  
  
}

// *************************************************************
// draw function

void draw() {
  // draw the background, title, and logo
  background(220,220,220);
  frame.setTitle("Open Source Flight Controller");
  
  // "red box" the "dirty" text fields to show that the display value
  // may not be the current value in the flight controller
  if (siteAltDirty) redBox(siteAltitudeText);
  if (goAltitudeDirty) redBox(goAltitudeText); 
  if (stageAltDirty) redBox(stageAltText); 
  if (stageVelDirty) redBox(stageVelText);
  if (mainAltDirty) redBox(mainAltText); 
  
  // ***********************************
  // This section draws most of the "non-GP4" text values in the window
  textFont(mono20);
  textAlign(LEFT, TOP);
  fill(0);
  
  // Display the current time
  text(timeString(), timeBox[0], timeBox[1]);
  
  // If we have a link, then we'll display the current flight controller values:
  if (theRocket.link_active()) {
    
    // Enable the controls
    enableControls();
    testEnable(theRocket.test_mode);
    armEnable(theRocket.arm_mode);
    
    // Set the Check boxes to the current flight controller values:
    mainCheck.setSelected(theRocket.do_main); 
    deployCheck.setSelected(theRocket.do_deploy); 
    stageCheck.setSelected(theRocket.do_stage); 
    chutePinCheck.setSelected(theRocket.chute_open);
    
    // Display Flight Controller time and Mode
    text(formatMillisShort(theRocket.time), milliBox[0], milliBox[1]);
    text(theRocket.modeText(), modeBox[0], modeBox[1]);
    
    // Display the Lat, Lon, and Altitude Data
    int posX = latBox[0] + width20;
    if (theRocket.lat < 10) posX += width20;
    text(theRocket.latString(), posX, latBox[1]);
    posX = lonBox[0];
    if (theRocket.lon < 100) posX += width20;
    if (theRocket.lon < 10) posX += width20;
    text(theRocket.lonString(), posX, lonBox[1]);
    posX = altBox[0];
    if (theRocket.p_alt < 1000) posX += 2 * width20;
    if (theRocket.p_alt < 100) posX +=  width20;
    if (theRocket.p_alt < 10) posX +=  width20;
    text(theRocket.pAltString(), posX, altBox[1]);
    posX = g_altBox[0];
    if (theRocket.g_alt < 1000) posX += 2 * width20;
    if (theRocket.g_alt < 100) posX +=  width20;
    if (theRocket.g_alt < 10) posX +=  width20;
    text(theRocket.gAltString(), posX, g_altBox[1]);
    posX = aglBox[0];
    if (theRocket.agl < 1000) posX += 2 * width20;
    if (theRocket.agl < 100) posX +=  width20;
    if (theRocket.agl < 10) posX +=  width20;
    text(theRocket.aglString(), posX, aglBox[1]);
    
    // Display "Launch Hold" if holding
    if (theRocket.launch_hold) text("Launch Hold", holdBox[0], holdBox[1]);
    
    // Display the FET Hold Status
    textAlign(RIGHT, TOP);
    fill(theRocket.FETcolor());
    text(theRocket.FETstatus(), width - 20, modeBox[1]);
    textAlign(LEFT, TOP);
    
    // *************************************
    // This section displays any new data received
    
    // Display sensor data in text box
    if (theRocket.newSensorData) {
      theRocket.newSensorData = false;
      displayText.clearText();
      displayText.setText("Sensor Data:", 0);
      displayText.setText("Pressure = " + str(theRocket.p), 1);
      displayText.setText("Pres. Int. = " + str(theRocket.p_int), 2);
      displayText.setText("Vp Int. = " + str(theRocket.v_int), 3);
      displayText.setText("X Accel = " + str(theRocket.X_ACCEL), 4);
      displayText.setText("Y Accel = " + str(theRocket.Y_ACCEL), 5);
      displayText.setText("Z Accel = " + str(theRocket.Z_ACCEL), 6);
      displayText.setText("FET 1 Monitor = " + str(theRocket.fet1_monitor), 7);
      displayText.setText("FET 2 Monisor = " + str(theRocket.fet2_monitor), 8);
    }
    
    // Display flight data in text box
    if (theRocket.newFlightData) {
      theRocket.newFlightData = false;
      displayText.clearText();
      displayText.setText("Flight Data:", 0);
      displayText.setText("Cal Time = " + formatMillis(theRocket.cal_time), 1);
      displayText.setText("Launch Time = " + formatMillis(theRocket.launch_time), 2);
      displayText.setText("Flight Time = " + formatMillis(theRocket.flight_time), 3);
      displayText.setText("Stage Time = " + formatMillis(theRocket.stage_time), 4);
      displayText.setText("Deploy 1 = " + formatMillis(theRocket.deploy1_time), 5);
      displayText.setText("Deploy 2 = " + formatMillis(theRocket.deploy2_time), 6);
      displayText.setText("Min. Pressure = " + str(theRocket.p_min), 7);
      displayText.setText("Site Pressure = " + str(theRocket.p_site), 8);
      displayText.setText("Site Altitude = " + str(theRocket.site_alt), 9);
    }
    /*
    // Display accelerometer data in text box
    if (theRocket.newAccelData) {
      theRocket.newAccelData = false;
      displayText.clearText();
      displayText.setText("Accelerometer Data:", 0);
      displayText.setText("X Accel = " + str(theRocket.X_ACCEL), 1);
      displayText.setText("Y Accel = " + str(theRocket.Y_ACCEL), 2);
      displayText.setText("Z Accel = " + str(theRocket.Z_ACCEL), 3);
      displayText.setText("Mean Ax = " + str(theRocket.meanAx), 4);
      displayText.setText("Mean Ay = " + str(theRocket.meanAy), 5);
      displayText.setText("Mean Az = " + str(theRocket.meanAz), 6);
      displayText.setText("Sq Ax = " + str(theRocket.sqAx), 7);
      displayText.setText("Sq Ay = " + str(theRocket.sqAy), 8);
      displayText.setText("Sq Az = " + str(theRocket.sqAz), 9);
    }
    */
    // Display settings in text box & update text fields if requred
    if (theRocket.newSettings) {
      theRocket.newSettings = false;
      if (displaySetting) {
        displaySetting = false;
        displayText.clearText();
        displayText.setText("Setting:", 0);
        displayText.setText("Main Deploy P = " + str(theRocket.p_deploy), 1);
        displayText.setText("Go @ Pressure = " + str(theRocket.p_launch), 2);
        displayText.setText("Stage Go @ Pressure = " + str(theRocket.p_stage), 3);
        displayText.setText("Stage Go @ Vp = " + str(theRocket.pv_stage), 4);
        displayText.setText("Main Deploy Alt = " + str(theRocket.h_deploy), 5);
        displayText.setText("Go Altitude = " + str(theRocket.h_launch), 6);
        displayText.setText("Stage Go Alt. = " + str(theRocket.h_stage), 7);
        displayText.setText("Stage Go Vel = " + str(theRocket.v_stage), 8);
      }
      
      // ****************************
      // This section updates the text fields with settings data, if required
      if (siteAltUpdate) {
        siteAltitudeText.setText(str(theRocket.site_alt));
        siteAltDirty = false;
        siteAltUpdate = false;
      }
      if (goAltUpdate) {
        goAltitudeText.setText(str(theRocket.h_deploy)); 
        goAltitudeDirty = false;
        goAltUpdate = false;
      }
      if (stageAltUpdate) {
        stageAltText.setText(str(theRocket.h_stage)); 
        stageAltDirty = false;
        stageAltUpdate = false;
      }
      if (stageVelUpdate) {
        stageVelText.setText(str(theRocket.v_stage));
        stageVelDirty = false;
        stageVelUpdate = false;
      }
      if (mainAltUpdate) {
        mainAltText.setText(str(theRocket.h_deploy));
        mainAltDirty = false;
        mainAltUpdate = false;
      } 
    }
  } else {
    // Disable the control widgets if the Flight Controller link is down
    disableControls();
  }
  
  // Display the Link Status
  fill(theRocket.linkColor());
  text(theRocket.linkStatus(), linkBox[0], linkBox[1]);
  
  // Draw dividing lines on the display
  line(20, 150, 700, 150);
  line(20, 300, 700, 300);
  
  // Draw the text box in the GUI window
  displayText.draw();

}

// *************************************************************
// serialEvent callback function
//    This is attached to Serial.bufferUntil(), and re-directs
//    to the Rocket object's processOSC() method, which parses
//    the SerialOSC message and processes the result.

void serialEvent(Serial p) { 
  if (theRocket.have_port) {
    theRocket.processOSC();  // have a port, so process the OSC message
  } else {
    theRocket.readString();  // don't have a port yet - so process at lower level
  }
}


